package cn.com.dbappsecurity.frame.scanner;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.Modifier;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import lombok.Getter;
import lombok.Setter;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.lang.StringUtils;

import cn.com.dbappsecurity.frame.annotations.Param;
import cn.com.dbappsecurity.frame.annotations.Validate;
import cn.com.dbappsecurity.frame.paramparse.IParamParse;
import cn.com.dbappsecurity.frame.paramparse.ParamBoolean;
import cn.com.dbappsecurity.frame.paramparse.ParamByte;
import cn.com.dbappsecurity.frame.paramparse.ParamChart;
import cn.com.dbappsecurity.frame.paramparse.ParamDouble;
import cn.com.dbappsecurity.frame.paramparse.ParamFile;
import cn.com.dbappsecurity.frame.paramparse.ParamFloat;
import cn.com.dbappsecurity.frame.paramparse.ParamInteger;
import cn.com.dbappsecurity.frame.paramparse.ParamLong;
import cn.com.dbappsecurity.frame.paramparse.ParamRequest;
import cn.com.dbappsecurity.frame.paramparse.ParamResponse;
import cn.com.dbappsecurity.frame.paramparse.ParamSession;
import cn.com.dbappsecurity.frame.paramparse.ParamShort;
import cn.com.dbappsecurity.frame.paramparse.ParamString;
/**
 * 记录每一个controller信息
 * @author dhz
 */
public class Controllers{

	/*
	 * controller实例
	 */
	@Getter
	@Setter
	private Object doInstance;
	/*
	 * 保存方法信息
	 */
	private Map<String,Actions> methods = new HashMap<String,Actions>();
	
	/**
	 * 添加方法
	 * @param cls
	 * @param methodName
	 * @param method
	 * @throws Exception
	 */
	public void registerMethod(Class<?> cls,String methodName,Method method) throws Exception{
		//判断是否存在相同的方法名
		if(methods.get(methodName) != null){
			throw new Exception("controller["+cls.getSimpleName()+"],action[" + methodName + "]冲突");
		}
		Actions actions = new Actions();
		
		String[] paramNames = getParamNames(cls,method.getName());
		Annotation[][] paramAns = method.getParameterAnnotations();
		Class<?>[] types = method.getParameterTypes();
		List<IParamParse> paramParses = new ArrayList<IParamParse>();
		double fileMaxLen = 1024*1024*1;
		for(int i=0;i<types.length;i++){
			//参数名,有Param注解则用Param注解的名称
			Param pm = getParamAnnotation(paramAns[i]);
			boolean paramTrim = true;
			if(pm != null){
				paramTrim = pm.trim();
				if(!StringUtils.isBlank(pm.value())){
					paramNames[i] = pm.value();
				}
			}
			Validate vd = getValidate(paramAns[i]);
			if(vd != null && vd.maxLen() > fileMaxLen){
				fileMaxLen = vd.maxLen();
			}
			IParamParse paramParse = null;
			Class<?> clasz = types[i];
			if(clasz == FileItem.class){
				paramParse = new ParamFile(paramNames[i],clasz.isArray(),vd);
			}else if(clasz == HttpServletRequest.class){
				paramParse = new ParamRequest();
			}else if(clasz == HttpServletResponse.class){
				paramParse = new ParamResponse();
			}else if(clasz == HttpSession.class){
				paramParse = new ParamSession();
			}else if(clasz == String.class){
				paramParse = new ParamString(paramTrim,paramNames[i],clasz.isArray(),vd);
			}else if(clasz == Boolean.class || clasz == boolean.class || clasz == Boolean[].class || clasz == boolean[].class){
				paramParse = new ParamBoolean(paramNames[i],clasz.isArray(),vd);
			}else if(clasz == Byte.class || clasz == byte.class || clasz == Byte[].class || clasz == byte[].class){
				paramParse = new ParamByte(paramNames[i],clasz.isArray(),vd);
			}else if(clasz == Character.class || clasz == char.class || clasz == Character[].class || clasz == char[].class){
				paramParse = new ParamChart(paramNames[i],clasz.isArray(),vd);
			}else if(clasz == Double.class || clasz == double.class || clasz == Double[].class || clasz == double[].class){
				paramParse = new ParamDouble(paramNames[i],clasz.isArray(),vd);
			}else if(clasz == Float.class || clasz == float.class || clasz == Float[].class || clasz == float[].class){
				paramParse = new ParamFloat(paramNames[i],clasz.isArray(),vd);
			}else if(clasz == Integer.class || clasz == int.class || clasz == Integer[].class || clasz == int[].class){
				paramParse = new ParamInteger(paramNames[i],clasz.isArray(),vd);
			}else if(clasz == Long.class || clasz == long.class || clasz == Long[].class || clasz == long[].class){
				paramParse = new ParamLong(paramNames[i],clasz.isArray(),vd);
			}else if(clasz == Short.class || clasz == short.class || clasz == Short[].class || clasz == short[].class){
				paramParse = new ParamShort(paramNames[i],clasz.isArray(),vd);
			}
			paramParses.add(paramParse);
		}
		actions.setMethod(method);
		actions.setMaxLen(fileMaxLen);
		actions.setParams(paramParses);
		methods.put(methodName,actions);
	}
	
	/**
	 * 获取controller方法信息
	 * @param methodName
	 * @return
	 */
	public Actions getMethod(String methodName){
		return methods.get(methodName);
	}
	
	/*
	 * 获取方法参数名
	 * @param clz
	 * @param methodName
	 * @return
	 * @throws Exception
	 */
	private String[] getParamNames(Class<?> clz,String methodName) throws Exception{
		ClassPool pool = ClassPool.getDefault();
		pool.insertClassPath(new ClassClassPath(clz));
		CtClass ctClass = pool.get(clz.getName());
		CtMethod cm = ctClass.getDeclaredMethod(methodName);
		CodeAttribute codeAttribute = cm.getMethodInfo().getCodeAttribute();
		LocalVariableAttribute attr = (LocalVariableAttribute)codeAttribute.getAttribute(LocalVariableAttribute.tag);
		
		String[] variableNames = new String[cm.getParameterTypes().length];
		int staticIndex = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
		for (int i = 0; i < variableNames.length; i++)
			variableNames[i] = attr.variableName(i + staticIndex);
		
		return variableNames;
	}
	
	/*
	 * 获取Param注解
	 * @param an
	 * @return
	 */
	private Param getParamAnnotation(Annotation[] an){
		for(Annotation a : an){
			if(a instanceof Param){
				return (Param)a;
			}
		}
		return null;
	}
	
	/*
	 * 获取Validate注解
	 * @param an
	 * @return
	 */
	private Validate getValidate(Annotation[] an){
		for(Annotation a : an){
			if(a instanceof Validate){
				return (Validate)a;
			}
		}
		return null;
	}
}
